package org.hghog.jlinkdemo.process;

import org.hghog.jlinkdemo.util.OperatorInfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author : HGhog
 * @date : 2024-08-27 14:38
 * @version: 1.0
 */
public class MainProcess {

    private final static String PROJECT_DIR = System.getProperty("user.dir") +File.separator;
    // private final static String PROJECT_DIR = "";

    private final static List<String> TYPES = new ArrayList<>();

    static {
        // 获取支持转化的视频类型
        try (InputStream stream = MainProcess.class.getResourceAsStream("mime.types")) {
            byte[] bytes = Objects.requireNonNull(stream).readAllBytes();
            String mimeType = new String(bytes, StandardCharsets.UTF_8);
            String[] split = mimeType.split("\n");
            TYPES.addAll(List.of(split));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private MainProcess() {

    }

    public static void start(OperatorInfo info) {
        List<FileBean> beans;
        beans = checkFiles(info);
        beans = startCut(beans);
        beans = startSplit(beans);
        showErrorMessage(beans);
    }

    /**
     * 开始执行剪辑操作
     */
    private static List<FileBean> startCut(List<FileBean> fileBeans) {
        System.out.println("-------------------------------------------剪辑执行开始-------------------------------------------");
        executor(fileBeans);
        System.out.println("-------------------------------------------剪辑执行完成-------------------------------------------");
        return fileBeans;
    }

    /**
     * 开始执行分片操作
     */
    private static List<FileBean> startSplit(List<FileBean> fileBeans) {
        // 过滤出已经剪辑成功的视频
        List<FileBean> beans = fileBeans.stream().filter(bean -> !bean.isError()).toList();

        // 操作需要分片的视频
        List<FileBean> children = new ArrayList<>();
        for (FileBean parent : beans) {
            if (!parent.isSplit()) continue;

            // 分片总数不能小于视频时长
            long times = parent.getEnd();
            byte frame = parent.getSplitSum();
            if (times < frame) {
                FileBean child = new FileBean(true, parent.getPath() + " => 视频时长为 { " + times + " } < 分片数量 { " + frame + " }");
                children.add(child);
                continue;
            }

            // 创建分片文件夹
            String parentName = parent.getName();
            String splitDir = parent.getOutPath().substring(0, parent.getOutPath().lastIndexOf("."));
            File folder = new File(splitDir);
            boolean mkdir = folder.mkdir();
            if (!mkdir) {
                FileBean child = new FileBean(true, parent.getPath() + " => 文件夹 { " + mkdir + " } 创建失败");
                children.add(child);
                continue;
            }

            // 构建分片视频
            for (int i = 0; i < frame; i++) {
                long var1;
                long end;
                if (i + 1 == frame) {
                    var1 = i * (times / frame);
                    end = times - var1;
                } else {
                    var1 = i * (times / frame);
                    end = times / frame;
                }
                String begin = LocalTime.ofSecondOfDay(var1).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                String childName = parentName.substring(0, parentName.lastIndexOf(".")) + ("_" + i) + parentName.substring(parentName.lastIndexOf("."));
                FileBean child = new FileBean(childName, parent.getOutPath(), times, begin, end);
                child.setOutPath(splitDir + File.separator + childName);
                child.setSplit(parent.isSplit());
                children.add(child);
            }
            parent.setSplit(!parent.isSplit());
        }
        if (children.isEmpty()) return fileBeans;
        System.out.println("-------------------------------------------分片执行开始-------------------------------------------");
        executor(children);
        System.out.println("-------------------------------------------分片执行完成-------------------------------------------");
        fileBeans.addAll(children);
        return fileBeans;
    }

    private static void executor(List<FileBean> fileBeans) {
        Runtime runtime = Runtime.getRuntime();
        for (FileBean file : fileBeans) {
            // 不执行错误文件
            if (file.isError()) continue;

            StringBuilder builder = new StringBuilder(PROJECT_DIR);
            builder.append("ffmpeg -ss ");
            builder.append(file.getBegin());
            builder.append(" -i ");
            builder.append("\"");
            builder.append(file.getPath());
            builder.append("\"");
            builder.append(" -hide_banner -c copy -t ");
            builder.append(file.getEnd());
            builder.append(" \"");
            builder.append(file.getOutPath());
            builder.append("\"");
            System.out.println(builder);
            try {
                Process exec = runtime.exec(builder.toString());
                BufferedReader error = exec.errorReader(StandardCharsets.UTF_8);
                String line = null;
                while ((line = error.readLine()) != null) {
                    System.out.println(line);
                }
                error.close();
                exec.destroy();
            } catch (Exception e) {
                file.setError(true);
                file.setErrorMessage(e.getMessage());
            }
        }
    }

    /**
     * 校验文件可行性
     */
    private static List<FileBean> checkFiles(OperatorInfo info) {
        File folder = new File(info.getInPath());
        File[] files = folder.listFiles();
        if (files == null || files.length == 0) {
            return new ArrayList<>();
        }
        System.out.println("-------------------------------------------文件校验开始-------------------------------------------");
        List<FileBean> fileBeans = checkVideo(files, info);

        Runtime runtime = Runtime.getRuntime();
        for (FileBean file : fileBeans) {
            // 不执行错误文件
            if (file.isError()) continue;

            StringBuilder builder = new StringBuilder(PROJECT_DIR);
            builder.append("ffmpeg");
            builder.append(" -i ");
            builder.append("\"");
            builder.append(file.getPath());
            builder.append("\"");
            builder.append(" -hide_banner");
            try {
                // 执行executor
                Process exec = runtime.exec(builder.toString());

                // 计算视频时间
                InputStream errorStream = exec.getErrorStream();
                byte[] bytes = errorStream.readAllBytes();
                String meta = new String(bytes, StandardCharsets.UTF_8);
                meta = meta.replaceAll(" ", "");
                int index = meta.indexOf("Duration");
                String duration = meta.substring(index + 9, index + 17);
                LocalTime localTime = LocalTime.parse(duration, DateTimeFormatter.ofPattern("HH:mm:ss"));
                long times = localTime.getLong(ChronoField.SECOND_OF_DAY);

                // 判断剪辑时长是否超过视频时长
                long needTime = info.getBegin() + info.getEnd();
                if (times < needTime) {
                    file.setError(true);
                    file.setErrorMessage(file.getPath() + " => 视频时长为 { " + times + " } < 剪辑的时长 { " + needTime + " }");
                } else {
                    String begin = localTime.minusSeconds(times - info.getBegin()).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                    long end = times - info.getBegin() - info.getEnd();
                    file.setTime(times);
                    file.setBegin(begin);
                    file.setEnd(end);
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
        System.out.println("-------------------------------------------文件校验完毕-------------------------------------------");
        return fileBeans;
    }

    /**
     * 过滤出视频文件
     */
    private static List<FileBean> checkVideo(File[] files, OperatorInfo info) {
        List<FileBean> fileBeans = new ArrayList<>();
        for (File file : files) {
            String name = file.getName();
            int index = name.lastIndexOf('.');
            if (index == -1) {
                fileBeans.add(new FileBean(true, file.getAbsolutePath() + " => 非视频文件"));
                continue;
            }
            String suffix = name.substring(index + 1);
            if (TYPES.stream().noneMatch(type -> type.contains(suffix.toLowerCase()))) {
                fileBeans.add(new FileBean(true, file.getAbsolutePath() + " => 不支持该类型 { " + suffix + " } 进行操作"));
                continue;
            }
            // 剪辑片头片尾视频
            FileBean fileBean = new FileBean(file.getName(), file.getAbsolutePath());
            String outPath = info.getOutPath() + File.separator;
            if (info.isToType()) {
                outPath += name.substring(0, index + 1) + info.getType().toLowerCase();
            } else {
                outPath += file.getName();
            }
            fileBean.setOutPath(outPath);
            fileBeans.add(fileBean);

            // 分片视频
            fileBean.setSplit(info.isToSplit());
            fileBean.setSplitSum(Byte.parseByte(info.getSplit()));
        }
        return fileBeans;
    }

    /**
     * 展示 错误文件信息
     */
    private static void showErrorMessage(List<FileBean> files) {
        System.out.println("-------------------------------------------成功信息展示-------------------------------------------");
        files.stream().filter(file -> !file.isError()).forEach(file -> {
            String banner = file.isSplit() ? "[ 分片成功: " : "[ 剪辑成功: ";
            System.out.println(banner + file.getOutPath() + " ]");
        });
        System.out.println("-------------------------------------------成功信息展示-------------------------------------------");
        System.out.println("-------------------------------------------错误信息展示-------------------------------------------");
        files.stream().filter(FileBean::isError).forEach(file -> {
            String banner = file.isSplit() ? "[ 分片失败: " : "[ 剪辑失败: ";
            System.out.println(banner + file.getErrorMessage() + " ]");
        });
        System.out.println("-------------------------------------------错误信息展示-------------------------------------------");
    }

}

